/*******************************************************************************
 * Copyright 2007, 2009 InnovaSoft SA http://www.innovasoft.biz/
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
 
package biz.innovasoft.nodb.index;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.NumberTools;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MultiSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SingleInstanceLockFactory;

import biz.innovasoft.nodb.RepositoryException;
import biz.innovasoft.nodb.query.QueryCriteria;

public class Searcher {

	private transient IndexManager _index;
	private IndexSearcher []searchers;
	
	public Searcher () {}
	
	public Searcher(IndexManager index) {
		this(index, false);
	}
	
	public Searcher(IndexManager index, boolean stricted) {
		if (index.getDirectory().getLockFactory() == null) {
			index.getDirectory()
					.setLockFactory(new SingleInstanceLockFactory());
		}
		_index = index;

		if (stricted) {
			initializeStricted();
		} else {
			initialize();
		}
	}

	private void initialize () throws RepositoryException {
		List<IndexSearcher> srchrs = new ArrayList<IndexSearcher>();
		try {
			List<Directory> dirs = _index.getDirectoriesFromChilds();
			for (Directory dir : dirs) {
				srchrs.add(new IndexSearcher(dir));
			}
		} catch (IOException e) {
			throw new RepositoryException ("Error initializing searcher.", e);
		}
		searchers = (IndexSearcher[])srchrs.toArray(new IndexSearcher[0]);
	}
	
	private void initializeStricted () throws RepositoryException {
		try {
			searchers = new IndexSearcher[1];
			searchers[0] = new IndexSearcher (_index.getDirectory()); 
		} catch (IOException e) {
			throw new RepositoryException ("Error initializing searcher.", e);
		}
		
	}
	
	public SearcherResult searchObject(QueryCriteria query) {
		String querystr = query.getExpressionString();
		Sort sort = new Sort(query.getSortFields());
		SearcherResult result = new SearcherResult ();
		
		try {
			int from = 0;
			if ( query.getStartAt() > 0) {
				from = query.getStartAt();
			} else {
				from = (query.getPage() == 0) ? 0 : query.getPage() * query.getMaxResults();
			}
			int to = from + query.getMaxResults();
			
			QueryParser parser = new QueryParser("field",
					new WhitespaceAnalyzer());
			parser.setLowercaseExpandedTerms(false);
			Query _query = null;
			try {
				_query = parser.parse(querystr);
			} catch (ParseException e) {
				throw new SyntaxErrorException ("Error parsing query.", e);
			}

			MultiSearcher searcher = new MultiSearcher (searchers);
			TopDocs docs = searcher.search(_query, null, to, sort);
			
			result.setTotalHits(docs.totalHits);
			if (docs.totalHits > 0) {
				if ( from > docs.totalHits ) return result;
				if ( to > docs.totalHits ) to = docs.totalHits;
				for (int t = from; t < to; t++) {
					Document fh = searcher.doc(docs.scoreDocs[t].doc);
					Long id = NumberTools.stringToLong(fh.getField("oid").stringValue());
					result.getOids().add(id);
				}
				result.setMinRange(from);
				result.setMaxRange(to);
				result.setMaxResults(query.getMaxResults());
			}

		} catch (Exception e) {
			throw new RepositoryException (e);
		}
		return result;
	}

	public SearcherResult searchObjectInMaster(QueryCriteria query) {
		Sort sort = new Sort(query.getSortFields());
		SearcherResult result = new SearcherResult ();
		
		try {
			int from = 0;
			if ( query.getStartAt() > 0) {
				from = query.getStartAt();
			} else {
				from = (query.getPage() == 0) ? 0 : query.getPage() * query.getMaxResults();
			}
			int to = from + query.getMaxResults();
			
			MatchAllDocsQuery allquery = new MatchAllDocsQuery();
			MultiSearcher searcher = new MultiSearcher (searchers);
			TopDocs docs = searcher.search(allquery, null, to, sort);
			
			result.setTotalHits(docs.totalHits);
			if (docs.totalHits > 0) {
				if ( from > docs.totalHits ) return result;
				if ( to > docs.totalHits ) to = docs.totalHits;
				for (int t = from; t < to; t++) {
					Document fh = searcher.doc(docs.scoreDocs[t].doc);
					Long id = NumberTools.stringToLong(fh.getField("oid").stringValue());
					result.getOids().add(id);
				}
				result.setMinRange(from);
				result.setMaxRange(to);
				result.setMaxResults(query.getMaxResults());
			}

		} catch (Exception e) {
			throw new RepositoryException (e);
		}
		return result;
	}
	
	
	public void deleteIndex(Long oid) {
		deleteIndex(oid, _index);
	}

	private void deleteIndex (Long oid, IndexManager index) {
		IndexSearcher searcher;
		try {
			searcher = new IndexSearcher(index.getDirectory());
		} catch (Exception e) {
			return;
		}
		try {
			searcher.getIndexReader().deleteDocuments(
						new Term("oid", NumberTools.longToString(oid)));
			searcher.close();
		} catch (Exception e) {
			throw new IndexException("Object with oid: " + oid
					+ ", could not be deleted from index.");
		}
	}
}
